/***************************************************************************************************
 * Copyright 2025-2025 x-tools-author(x-tools@outlook.com). All rights reserved.
 *
 * The file is encoded using "utf8 with bom", it is a part of xTools project.
 *
 * xFlow is licensed according to the terms in the file LICENCE(GPL V3) in the root of the source
 * code directory.
 **************************************************************************************************/
#include "nodeeditor.h"

#include <QHBoxLayout>

#include "nodeeditorruler.h"
#include "nodeeditorscene.h"
#include "nodeeditorview.h"

namespace xFlow {

struct NodeEditorParameterKeys
{
    const QString scale{"scale"};
    const QString model{"model"};
    const QString hScrollBarValue{"hScrollBarValue"};
    const QString vScrollBarValue{"vScrollBarValue"};
};

NodeEditor::NodeEditor(QWidget *parent)
    : QScrollArea(parent)
{
    m_view = new NodeEditorView(QColor(Qt::white), this);
    m_view->setObjectName("nodeEditorView");
    m_view->setStyleSheet("QWidget#nodeEditorView { border: none; }");
    m_view->setScaleRange(1.0, 1.0);
    setContentsMargins(0, 0, 0, 0);
    setWidget(m_view);
    setScale(1.0);
}

NodeEditor::~NodeEditor() {}

NodeEditorView *NodeEditor::view() const
{
    return m_view;
}

void NodeEditor::setRulerVisible(bool visible)
{
    if (m_hRuler == nullptr || m_vRuler == nullptr) {
        return;
    }

    m_hRuler->setVisible(visible);
    m_vRuler->setVisible(visible);
}

void NodeEditor::setRulerColor(const QColor &color)
{
    if (m_hRuler == nullptr || m_vRuler == nullptr) {
        return;
    }

    m_hRuler->setColor(color);
    m_vRuler->setColor(color);
}

void NodeEditor::setupRuler(NodeEditorRuler *hRuler, NodeEditorRuler *vRuler)
{
    m_hRuler = hRuler;
    m_vRuler = vRuler;
}

QJsonObject NodeEditor::save()
{
    QJsonObject parameters;
    NodeEditorParameterKeys keys;

    parameters.insert(keys.scale, m_scale);
    parameters.insert(keys.hScrollBarValue, horizontalScrollBar()->value());
    parameters.insert(keys.vScrollBarValue, verticalScrollBar()->value());
    parameters.insert(keys.model, m_view->model()->save());

    return parameters;
}

void NodeEditor::load(const QJsonObject &parameters)
{
    NodeEditorParameterKeys keys;
    int hValue = parameters.value(keys.hScrollBarValue).toInt(0);
    int vValue = parameters.value(keys.vScrollBarValue).toInt(0);
    horizontalScrollBar()->setValue(hValue);
    verticalScrollBar()->setValue(vValue);

    QJsonObject modelObject = parameters.value(keys.model).toObject();
    m_view->model()->load(modelObject);
    auto model = m_view->model();
    for (auto nodeId : model->allNodeIds()) {
        auto scene = m_view->cookedScene();
        scene->nodeGeometry().recomputeSize(nodeId);
    }

    m_scale = parameters.value(keys.scale).toDouble(1.0);
    setScale(m_scale);
}

void NodeEditor::zoomIn()
{
    m_scale += 0.1;

    if (m_scale > maxScale()) {
        m_scale = maxScale();
    }

    setScale(m_scale);
}

void NodeEditor::zoomOut()
{
    m_scale -= 0.1;

    if (m_scale < minScale()) {
        m_scale = minScale();
    }

    setScale(m_scale);
}

qreal NodeEditor::scale() const
{
    return m_scale;
}

void NodeEditor::setScale(qreal scale)
{
    if (scale < minScale()) {
        scale = minScale();
    }

    if (scale > maxScale()) {
        scale = maxScale();
    }

    m_scale = scale;

    static QRectF rect = NodeEditorView::defaultSceneRect();
    auto scene = m_view->scene();
    scene->setSceneRect(rect);
    QSizeF size = rect.size();
    size *= m_scale;
    m_view->setSceneRect(rect);
    m_view->setFixedSize(size.toSize());
    m_view->fitInView(rect);
    emit scaleChanged(scale);
}

qreal NodeEditor::minScale()
{
    return 0.5;
}

qreal NodeEditor::maxScale()
{
    return 2.0;
}

NodeEditorView *NodeEditor::view()
{
    return m_view;
}

} // namespace xFlow